package com.tangly.codeing.service.impl;

import com.tangly.codeing.model.Article;
import com.tangly.codeing.service.BaseSearchService;
import com.tangly.codeing.utils.PageInfo;
import com.tangly.codeing.utils.ReturnT;
import lombok.SneakyThrows;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryStringQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * elasticsearch 搜索引擎
 *
 * @author tangbing
 * @date 2019/10/31
 */
@Service
public class BaseSearchServiceImpl<T> implements BaseSearchService<T> {

    private Logger log = LoggerFactory.getLogger(getClass());
    @Resource
    private ElasticsearchTemplate elasticsearchTemplate;


    @Override
    public List<T> query(String keyword, Class<T> clazz) {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(new QueryStringQueryBuilder(keyword))
                .withSort(SortBuilders.scoreSort().order(SortOrder.DESC))
                // .withSort(new FieldSortBuilder("createTime").order(SortOrder.DESC))
                .build();

        return elasticsearchTemplate.queryForList(searchQuery, clazz);
    }

    /**
     * 高亮显示，返回分页
     */
    @Override
    public ReturnT<PageInfo> queryHitByPage(PageInfo pageInfo) {
        int pageNo = pageInfo.getCurrentPage();
        int pageSize = pageInfo.getPageSize();
        Map<String, Object> params = pageInfo.getWhereMap();
        String fields = params.get("fields").toString();
        String[] fieldNames = fields.split(",");
        String keyword = params.get("keyword").toString();
        String indexName = params.get("indexName").toString();

        // 构造查询条件,使用标准分词器.
        QueryBuilder matchQuery = createQueryBuilder(keyword, fieldNames);

        // 设置高亮,使用默认的highlighter高亮器
        HighlightBuilder highlightBuilder = createHighlightBuilder(fieldNames);

        // 设置查询字段
        SearchResponse response = elasticsearchTemplate.getClient().prepareSearch(indexName)
                .setQuery(matchQuery)
                .highlighter(highlightBuilder)
                .setFrom((pageNo - 1) * pageSize)
                .setSize(pageNo * pageSize)
                .get();


        // 返回搜索结果
        SearchHits hits = response.getHits();

        long totalCount = hits.getTotalHits();
        pageInfo.setTotalRecord((int) totalCount);
        pageInfo.setResultObject(getHitList(hits, fieldNames));
        return new ReturnT<>(pageInfo);
    }

    /**
     * 构造查询条件
     */
    private QueryBuilder createQueryBuilder(String keyword, String... fieldNames) {
        // 构造查询条件,使用标准分词器.
        return QueryBuilders.multiMatchQuery(keyword, fieldNames).operator(Operator.OR);
    }

    /**
     * 构造高亮器
     */
    private HighlightBuilder createHighlightBuilder(String... fieldNames) {
        // 设置高亮,使用默认的highlighter高亮器
        HighlightBuilder highlightBuilder = new HighlightBuilder()
                // .field("productName")
                .preTags("<span style='color:red'>")
                .postTags("</span>");

        // 设置高亮字段
        for (String fieldName : fieldNames) {
            highlightBuilder.field(fieldName);
        }
        return highlightBuilder;
    }

    /**
     * 处理高亮结果
     */
    private List<Map<String, Object>> getHitList(SearchHits hits, String[] fields) {
        List<Map<String, Object>> course = new ArrayList<>();
        for (SearchHit hit : hits) {
            // 获取高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            Map<String, Object> source = hit.getSourceAsMap();
            // 循环替换高亮字段
            for (String field : fields) {
                HighlightField highlightField = highlightFields.get(field);
                replaceField(highlightField, source);
            }
            course.add(source);
        }
        return course;
    }

    private void replaceField(HighlightField highlightField, Map<String, Object> source) {
        if (highlightField != null) {
            Text[] fragments = highlightField.fragments();
            StringBuilder name = new StringBuilder();
            for (Text text : fragments) {
                name.append(text);
            }
            // 高亮字段替换掉原本的内容
            source.put(highlightField.getName(), name.toString());
        }
    }

    @Override
    public void deleteIndex(String indexName) {
        elasticsearchTemplate.deleteIndex(indexName);
    }
}
